#include "base.pb.h"
#include "baseproto.h"
#include "cnsMacroGlobal.h"
#include <QtEndian>
#include <QDebug>

CNS_INIT(ProtoType)
qRegisterMetaType<ProtoType*>("ProtoType");
CNS_INIT_END

BaseProto::BaseProto()
{
    _size = _receiveSize = BEGIN_SIZE;
    _buf = static_cast<char *>(malloc(_size));
    _receiveBuf = static_cast<char *>(malloc(_size));
    _receiveBufTail = nullptr;
}

char *BaseProto::getBuf(uint32_t size)
{
    if(size > _size){
        while(size > _size)
        {
            _size *= ENLARGE_STEP;
        }
        delete _buf;
        _buf = static_cast<char *>(malloc(_size));
    }
    return _buf;
}

char *BaseProto::getReceiveBuf(uint32_t size)
{
    if(size > _receiveSize){
        while(size > _receiveSize)
        {
            _receiveSize *= ENLARGE_STEP;
        }
        delete _receiveBuf;
        _receiveBuf = static_cast<char *>(malloc(_receiveSize));
    }
    return _receiveBuf;
}

char* BaseProto::addToReceiveBuf(uint32_t &handleSize, char *buf, uint32_t size)
{
    if(_receiveBufTail)
    {
        uint32_t totalSize = static_cast<uint32_t>(_receiveBufTail - _receiveBuf) + size;

        if(totalSize == _curReceiveMsgSize){
            memcpy(_receiveBufTail, buf, size);
            handleSize = size;
            _receiveBufTail = nullptr;

            return _receiveBuf + 4;
        } else if(totalSize < _curReceiveMsgSize){
            memcpy(_receiveBufTail, buf, size);
            handleSize = size;
            _receiveBufTail = &_receiveBuf[totalSize];
        } else {//totalSize > _curReceiveMsgSize
            handleSize = size - totalSize+_curReceiveMsgSize;
            memcpy(_receiveBufTail, buf, handleSize);
            _receiveBufTail = nullptr;
            qDebug() << QString("sticky and partial package! receive size = %1, package size = %2").arg(size).arg(_curReceiveMsgSize);
            return _receiveBuf + 4;
        }
    } else{
        _curReceiveMsgSize = static_cast<uint32_t> ((buf[0] & 0xFF)
                | ((buf[1] & 0xFF) << 8)
                | ((buf[2] & 0xFF) << 16)
                | ((buf[3] & 0xFF) << 24));

        _curReceiveMsgSize = qFromLittleEndian(_curReceiveMsgSize);
        if(size >= _curReceiveMsgSize)
        {
            if(size > _curReceiveMsgSize)
            {
                qDebug() << QString("sticky package! receive size = %1, package size = %2").arg(size).arg(_curReceiveMsgSize);
            }
            handleSize = _curReceiveMsgSize;
            return buf+4;
        }

        char * receiveBuf = getReceiveBuf(_curReceiveMsgSize);
        _receiveBufTail = &receiveBuf[size];
        memcpy(receiveBuf, buf, size);
        handleSize = size;
    }
    return nullptr;
}

char *BaseProto::encode(unsigned int type, google::protobuf::Message& msg, uint32_t &size)
{
    static unsigned int sequence =0;
    cgproto::Message m;
    m.set_type(type);
    m.set_seq(sequence++);
    size = static_cast<uint32_t>(msg.ByteSizeLong());
    char *buf = getBuf(size+4);
    if(msg.SerializeToArray(_buf, static_cast<int>(_size))){
        m.set_body(_buf, size);
        if(m.SerializeToArray(_buf+4, static_cast<int>(_size-4))){
            size = static_cast<uint32_t>(m.ByteSizeLong()+4);
            uint32_t ts = qToLittleEndian(size);
            //Little-Endian
            _buf[3] = static_cast<char>((ts >> 24) & 0xFF);
            _buf[2] = static_cast<char>((ts >> 16) & 0xFF);
            _buf[1] = static_cast<char>((ts >> 8) & 0xFF);
            _buf[0] = static_cast<char>(ts & 0xFF);
            return _buf;
        }
    }
    return nullptr;
}

bool BaseProto::decode(const char *msg, google::protobuf::Message &m)
{
    return m.ParseFromArray(msg, static_cast<int>(_curReceiveMsgSize-4));
}

bool BaseProto::decode(const char *msg, int size, google::protobuf::Message &m)
{
    return m.ParseFromArray(msg, size);
}
